////////////////////////////////////////////////////////////////////////////////////////
//
//  Copyright 2018 Alexander Stukowski
//
//  This file is part of OVITO (Open Visualization Tool).
//
//  OVITO is free software; you can redistribute it and/or modify it either under the
//  terms of the GNU General Public License version 3 as published by the Free Software
//  Foundation (the "GPL") or, at your option, under the terms of the MIT License.
//  If you do not alter this notice, a recipient may use your version of this
//  file under either the GPL or the MIT License.
//
//  You should have received a copy of the GPL along with this program in a
//  file LICENSE.GPL.txt.  You should have received a copy of the MIT License along
//  with this program in a file LICENSE.MIT.txt
//
//  This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND,
//  either express or implied. See the GPL or the MIT License for the specific language
//  governing rights and limitations.
//
////////////////////////////////////////////////////////////////////////////////////////

#include <ovito/particles/gui/ParticlesGui.h>
#include <ovito/particles/modifier/analysis/StructureIdentificationModifier.h>
#include <ovito/stdobj/table/DataTable.h>
#include <ovito/gui/desktop/properties/ModifierPropertiesEditor.h>
#include "StructureListParameterUI.h"

namespace Ovito { namespace Particles {

IMPLEMENT_OVITO_CLASS(StructureListParameterUI);

/******************************************************************************
* Constructor.
******************************************************************************/
StructureListParameterUI::StructureListParameterUI(PropertiesEditor* parentEditor, bool showCheckBoxes)
	: RefTargetListParameterUI(parentEditor, PROPERTY_FIELD(StructureIdentificationModifier::structureTypes)),
	  _showCheckBoxes(showCheckBoxes)
{
#ifdef Q_OS_MAC
	int tableWidgetHeight = 240;
#else
	int tableWidgetHeight = 300;
#endif

	connect(tableWidget(tableWidgetHeight), &QTableWidget::doubleClicked, this, &StructureListParameterUI::onDoubleClickStructureType);
	connect(parentEditor, &PropertiesEditor::contentsReplaced, tableWidget(), &QTableView::resizeRowsToContents);
	tableWidget()->setAutoScroll(false);

	// Update the structure count and fraction columns of the data model whenever the structure identification
	// modifier has generated new results.
	if(ModifierPropertiesEditor* modifierEditor = dynamic_object_cast<ModifierPropertiesEditor>(parentEditor)) {
		connect(modifierEditor, &ModifierPropertiesEditor::modifierEvaluated, this, [this]() {
			updateStructureCounts();
			_model->updateColumns(2, 3);
		});
	}
}

/******************************************************************************
* This method is called when a new editable object has been activated.
******************************************************************************/
void StructureListParameterUI::resetUI()
{
	// Refresh our internal structure count list.
	updateStructureCounts();

	// Call base implementation.
	RefTargetListParameterUI::resetUI();

	// Clear initial selection by default.
	tableWidget()->selectionModel()->clear();
}

/******************************************************************************
* Obtains the current structure counts from the pipeline.
******************************************************************************/
void StructureListParameterUI::updateStructureCounts()
{
	if(ModifierPropertiesEditor* modifierEditor = dynamic_object_cast<ModifierPropertiesEditor>(editor())) {
		if(ModifierApplication* modApp = modifierEditor->modifierApplication()) {

			// Get the current data pipeline output generated by the modifier.
			const PipelineFlowState& state = modifierEditor->getModifierOutput();

			// Access the data table in the pipeline state containing the structure counts.
			if(const DataTable* table = state.getObjectBy<DataTable>(modApp, QStringLiteral("structures"))) {
				_structureCounts = table->getYStorage();
				if(_structureCounts && _structureCounts->dataType() == PropertyStorage::Int64)
					return;
			}
		}
	}
	_structureCounts.reset();
}

/******************************************************************************
* Returns a data item from the list data model.
******************************************************************************/
QVariant StructureListParameterUI::getItemData(RefTarget* target, const QModelIndex& index, int role)
{
	ElementType* stype = dynamic_object_cast<ElementType>(target);
	StructureIdentificationModifier* modifier = dynamic_object_cast<StructureIdentificationModifier>(editObject());

	if(stype && modifier) {
		if(role == Qt::DisplayRole) {
			if(index.column() == 1) {
				return stype->nameOrNumericId();
			}
			else if(index.column() == 2) {
				if(_structureCounts && stype->numericId() >= 0 && stype->numericId() < _structureCounts->size())
					return ConstPropertyAccess<qlonglong>(_structureCounts)[stype->numericId()];
			}
			else if(index.column() == 3) {
				if(_structureCounts && stype->numericId() >= 0 && stype->numericId() < _structureCounts->size()) {
					size_t totalCount = 0;
					ConstPropertyAccess<qlonglong> structureArray(_structureCounts);
					for(auto c : structureArray)
						totalCount += c;
					return QString("%1%").arg((double)structureArray[stype->numericId()] * 100.0 / std::max((size_t)1, totalCount), 0, 'f', 1);
				}
			}
			else if(index.column() == 4) {
				return stype->numericId();
			}
		}
		else if(role == Qt::DecorationRole) {
			if(index.column() == 0)
				return (QColor)stype->color();
		}
		else if(role == Qt::CheckStateRole && _showCheckBoxes) {
			if(index.column() == 0)
				return stype->enabled() ? Qt::Checked : Qt::Unchecked;
		}
	}
	return {};
}

/******************************************************************************
* Returns the model/view item flags for the given entry.
******************************************************************************/
Qt::ItemFlags StructureListParameterUI::getItemFlags(RefTarget* target, const QModelIndex& index)
{
	if(index.column() == 0 && _showCheckBoxes)
		return RefTargetListParameterUI::getItemFlags(target, index) | Qt::ItemIsUserCheckable;
	else
		return RefTargetListParameterUI::getItemFlags(target, index);
}

/******************************************************************************
* Sets the role data for the item at index to value.
******************************************************************************/
bool StructureListParameterUI::setItemData(RefTarget* target, const QModelIndex& index, const QVariant& value, int role)
{
	if(index.column() == 0 && role == Qt::CheckStateRole) {
		if(ElementType* stype = static_object_cast<ElementType>(objectAtIndex(index.row()))) {
			bool enabled = (value.toInt() == Qt::Checked);
			undoableTransaction(tr("Enable/disable structure type"), [stype, enabled]() {
				stype->setEnabled(enabled);
			});
			return true;
		}
	}

	return RefTargetListParameterUI::setItemData(target, index, value, role);
}

/******************************************************************************
* Is called when the user has double-clicked on one of the structure
* types in the list widget.
******************************************************************************/
void StructureListParameterUI::onDoubleClickStructureType(const QModelIndex& index)
{
	// Let the user select a color for the structure type.
	ElementType* stype = static_object_cast<ElementType>(selectedObject());
	if(!stype) return;

	QColor oldColor = (QColor)stype->color();
	QColor newColor = QColorDialog::getColor(oldColor, editor()->container());
	if(!newColor.isValid() || newColor == oldColor) return;

	undoableTransaction(tr("Change structure type color"), [stype, newColor]() {
		stype->setColor(Color(newColor));
	});
}

}	// End of namespace
}	// End of namespace
